package com.game.logic.fighting.actor;

import akka.actor.ActorRef;
import com.coment.cfg.luban.PhysicalStrengthCfg;
import com.coment.cfg.luban.RolesSkillCfg;
import com.coment.cfg.luban.Tables;
import com.coment.cfg.luban.constant.AIDCFG;
import com.coment.cfg.luban.constant.CurrencyType;
import com.coment.cfg.luban.constant.PhysicalAction;
import com.coment.cfg.luban.fight.AttrsAddition;
import com.coment.cfg.luban.fight.AttrsBuffCfg;
import com.coment.cfg.luban.items.EquipCfg;
import com.coment.cfg.luban.roles.CharacterBaseAttributeConfig;
import com.coment.cfg.luban.roles.NumericalConfig;
import com.coment.cfg.luban.roles.TbCharacterBaseAttributeConfig;
import com.coment.cfg.luban.skill.SkillLvCostCfg;
import com.coment.game.fight.Attribute;
import com.coment.game.fight.AttributeManagerLogic;
import com.coment.game.fight.Attrs;
import com.coment.message.BuffInfo;
import com.coment.message.HurtInfo;
import com.coment.message.RolesActionType;
import com.coment.message.account.PayInfo;
import com.coment.message.account.ReceiveInfo;
import com.coment.message.core.ActionResult;
import com.coment.message.roles.*;
import com.coment.po.UserAccount;
import com.coment.po.roles.CultivatePanel;
import com.coment.po.roles.RolePanel;
import com.coment.po.roles.Roles;
import com.game.framework.ActorManager;
import com.game.framework.lock.LooksManager;
import com.game.framework.manager.core.ActorDaoAble;
import com.game.logic.fighting.attrs.FightingLogic;
import com.game.logic.fighting.skill.cd.CDHandle;
import com.game.logic.items.account.UserAccountActor;
import com.game.logic.roles.RolesCultivateLogic;
import com.xs.action.exection.ActionException;
import com.xs.build.server.websocket.channel.ClientManager;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
public class RolesActor extends ActorDaoAble {
    private final String rolesId;
    private Roles roles;

    public RolesActor(String rolesId) {
        if (rolesId == null) {
            throw new RuntimeException("角色ID不能为空");
        }
        this.rolesId = rolesId;
    }

    public static ActorRef getRolesActor(String rolesId) {
        if (rolesId == null) {
            throw new RuntimeException("角色ID不能为空");
        }
        return ActorManager.getActor(RolesActor.class, rolesId);
    }

    @Override
    public Receive createReceive() {
        return receiveBuilder()
                .match(Action.class, this::handleRolesAction)
                .match(SkillInfo.class, this::releaseSkillInfo)
                .match(BuffInfo.class, this::receivedBuff)
                .match(HurtInfo.class, this::handleHurtInfo)
                .match(UpgradeSkill.class, this::handleUpgradeSkill)
                .match(Equip.class, this::handleEquip)
                .match(ObtainExp.class, this::handleObtainExp)
                .match(ExitPVE.class, this::handleExitPVE)
                .match(StrengthAction.class, this::checkStamina)
                .match(Resurrection.class, this::handleResurrection)
                .match(RolesUpdatePush.class, this::handleRolesUpdatePush)
                .build();
    }

    private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2, Thread.ofVirtual().factory());
    public final Map<String, ScheduledFuture<?>> taskMap = new ConcurrentHashMap<>();


    private boolean inCultivate = false;

    private void handleRolesAction(Action<?> action) {
        RolesCmd cmd = RolesCmd.fomated(action.getCmd());
        switch (cmd) {
            case START_Cultivate:
                if (!inCultivate) {
                    inCultivate = true;
                    log.info("角色:{} 开始修炼", rolesId);
                    // 定时任务
                    Runnable runnable = () -> {
                        this.roles = rolesDao.findByIdMust(rolesId);
                        Double practiceSpeed = this.roles.rolePanel.getAttribute().get(AIDCFG.cultivationSpeed);
                        log.info("尝试修炼,角色:{},修炼速度:{}", rolesId, practiceSpeed);
                        roles.rolePanel.cultivatePanel.base.merge(AIDCFG.exp, practiceSpeed, Double::sum);
                        rolesDao.save(roles);
                    };
                    ScheduledFuture<?> scheduledTask = scheduler.schedule(runnable, 1, TimeUnit.MINUTES);
                    taskMap.put(rolesId, scheduledTask);
                }
                break;
            case STOP_Cultivate:
                if (inCultivate) {
                    inCultivate = false;
                    ScheduledFuture<?> remove = taskMap.remove(rolesId);
                    if (remove != null) {
                        remove.cancel(true);
                    }
                    log.info("角色ID: {} 停止修炼", rolesId);
                }
                break;
        }
    }

    private static final ConcurrentHashMap<String, CultivatePanel> lastCultivatePanel = new ConcurrentHashMap<>();
    private static final ConcurrentHashMap<String, Attrs> lastRolesAttrs = new ConcurrentHashMap<>();

    private void handleRolesUpdatePush(
            RolesUpdatePush updateMessage
    ) {
        this.roles = rolesDao.findByIdMust(rolesId);
        updateMessage.setUserInfoId(roles.getUserInfoId());
        updateMessage.setCardId(roles.cardId);


        CultivatePanel currentPanel = this.roles.rolePanel.cultivatePanel;
        CultivatePanel previousCultivatePanel = lastCultivatePanel.getOrDefault(rolesId, currentPanel);

        /// 获取变化-属性
        Attrs newAttrs = this.roles.rolePanel.getAttribute();
        Attrs previousAttrs = lastRolesAttrs.computeIfAbsent(rolesId, k -> newAttrs);
        Attrs updateAttrs = new Attrs();
        newAttrs.forEach((id, newValue) -> {
            NumericalConfig config = Tables.cfg.get_tbnumericalconfig().get(id);
            if (config.IsPush == 1) {
                Double oldValue = previousAttrs.get(id);
                if (oldValue == null) {
                    updateAttrs.put(id, newValue);
                    return;
                }
                double abs = Math.abs(newValue - oldValue);
                if (abs >= 0.1) {
                    updateAttrs.put(id, newValue);
                }
            }
        });
        updateMessage.setAttrs(updateAttrs);
        lastRolesAttrs.put(rolesId, newAttrs);

        if (!currentPanel.equals(previousCultivatePanel)) {
            /// 获取变化-物品栏
            Map<Integer, Set<Integer>> updateInventory = new HashMap<>();
            for (Map.Entry<Integer, Set<Integer>> integerSetEntry : currentPanel.inventory.entrySet()) {
                Integer id = integerSetEntry.getKey();
                Set<Integer> newSet = integerSetEntry.getValue();
                Set<Integer> oldSet = previousCultivatePanel.inventory.get(id);
                if (!newSet.equals(oldSet)) {
                    updateInventory.put(id, newSet);
                }
            }
            updateMessage.setInventory(updateInventory);
            lastCultivatePanel.put(rolesId, currentPanel);
        }


        if (!updateAttrs.isEmpty() || updateMessage.getInventory() != null) {
            int recentlyPlayedAnchorInfoId = userPlayInfoDao.findByUserInfoIdOrCreate(roles.getUserInfoId()).getRecentlyPlayedAnchorInfoId();
            ClientManager.sendMessageToClient(recentlyPlayedAnchorInfoId, updateMessage);
        }
    }

    ///  复活
    private void handleResurrection(Resurrection resurrection) {
        this.roles = rolesDao.findByIdMust(rolesId);
        int recentlyPlayedAnchorInfoId = userPlayInfoDao.findByUserInfoIdOrCreate(roles.getUserInfoId()).getRecentlyPlayedAnchorInfoId();
        RolesActionMessage rolesActionMessage = new RolesActionMessage(
                roles.getUserInfoId(),
                rolesId,
                roles.getCardId(),
                RolesActionType.RESURRECTION.getValue()
        );


        if (roles.isSurvive()) {
            rolesActionMessage.setType(RolesActionType.RESURRECTION_Failure.getValue());
            rolesActionMessage.setMessage("未死亡");
            ClientManager.sendMessageToClient(recentlyPlayedAnchorInfoId, rolesActionMessage);
            return;
        }

        /// 体力不足
        StrengthAction action = new StrengthAction(2, PhysicalAction.rolesDeath);
        ActionResult<Integer> result = checkStamina(action);
        if (!result.isOk()) {
            rolesActionMessage.setType(RolesActionType.RESURRECTION_Failure.getValue());
            rolesActionMessage.setMessage(result.message());
            ClientManager.sendMessageToClient(recentlyPlayedAnchorInfoId, rolesActionMessage);
            return;
        }

        /// 可以复活
        roles.rolePanel.fightPanel.fight.clear();
        roles.rolePanel.statusPanel.status.clear();
        rolesDao.save(roles);
        rolesActionMessage.attrs = roles.rolePanel.getAttribute();
        rolesActionMessage.attrs.put(AIDCFG.fightingCapacity, RolesCultivateLogic.tryCountFightingCapacity(roles));
        rolesActionMessage.strength = roles.rolePanel.cultivatePanel.base.get(AIDCFG.stamina).intValue();
        ClientManager.sendMessageToClient(recentlyPlayedAnchorInfoId, rolesActionMessage);
    }


    /// 体力行为
    private ActionResult<Integer> checkStamina(StrengthAction action) {
        ActionResult<Integer> actionResult = null;
        try {
            this.roles = rolesDao.findByIdMust(rolesId);

            Double currentStrength = roles.rolePanel.cultivatePanel.base.get(AIDCFG.stamina);
            Double lastRecoveryTime = roles.rolePanel.cultivatePanel.base.get(AIDCFG.lastTimeStaminaRecoveryTime);

            int currentTime = (int) (System.currentTimeMillis() / 1000); // 转换为秒

            PhysicalStrengthCfg cfg = Tables.cfg.get_tbphysicalstrengthcfg().data();
            int hourlyRecovery = cfg.hourlyRecovery;
            int maxStrength = cfg.initAndMax;

            // 计算自然恢复的体力
            int timePassed = (int) (currentTime - lastRecoveryTime);
            int recoveredStrength = (timePassed / 3600) * hourlyRecovery; // 每小时恢复

            // 更新体力值，不超过最大值
            int newStrength = (int) Math.min(currentStrength + recoveredStrength, maxStrength);

            switch (action.getType()) {
                case 1:
                    getSender().tell(newStrength, self());
                    // 保存
                    roles.rolePanel.cultivatePanel.base.put(AIDCFG.stamina, (double) newStrength);
                    roles.rolePanel.cultivatePanel.base.put(AIDCFG.lastTimeStaminaRecoveryTime, (double) currentTime);
                    rolesDao.save(roles);
                    actionResult = new ActionResult<>(200, newStrength, "isOk");
                    break;
                case 2:
                    newStrength += cfg.value.get(action.getValue());
                    newStrength = Math.min(newStrength, maxStrength);
                    if (newStrength < 0) {
                        actionResult = new ActionResult<>(500, newStrength, "体力不足" + newStrength);
                    } else {
                        roles.rolePanel.cultivatePanel.base.put(AIDCFG.stamina, (double) newStrength);
                        roles.rolePanel.cultivatePanel.base.put(AIDCFG.lastTimeStaminaRecoveryTime, (double) currentTime);
                        rolesDao.save(roles);
                        actionResult = new ActionResult<>(200, newStrength, "行动成功");
                    }
                    break;
                case 3:
                    newStrength += action.getValue();
                    newStrength = Math.min(newStrength, maxStrength);
                    roles.rolePanel.cultivatePanel.base.put(AIDCFG.stamina, (double) newStrength);
                    roles.rolePanel.cultivatePanel.base.put(AIDCFG.lastTimeStaminaRecoveryTime, (double) currentTime);
                    rolesDao.save(roles);
                    actionResult = new ActionResult<>(200, newStrength, "成功恢复体力");
                    break;
            }
        } catch (Exception e) {
            log.error("体力异常", e);
        }
        getSender().tell(actionResult, self());
        return actionResult;
    }

    /// 退出PVE
    private void handleExitPVE(ExitPVE exitPVE) {
        this.roles = rolesDao.findByIdMust(rolesId);
        log.info("{} 退出PVE", roles.userInfoId);
        roles.rolePanel.statusPanel.clear();
        roles.rolePanel.fightPanel.clear();
        rolesDao.save(roles);
    }

    /// 获得经验
    private void handleObtainExp(ObtainExp obtainExp) {
        this.roles = rolesDao.findByIdMust(rolesId);
        roles.rolePanel.cultivatePanel.base.merge(AIDCFG.exp, obtainExp.getExp(), Double::sum);

        Integer userInfoId = roles.getUserInfoId();
        RolePanel rolePanel = roles.getRolePanel();
        double level = rolePanel.cultivatePanel.base.get(AIDCFG.level);
        double exp = rolePanel.cultivatePanel.base.get(AIDCFG.exp);

        TbCharacterBaseAttributeConfig config = Tables.cfg.get_tbcharacterbaseattributeconfig();
        CharacterBaseAttributeConfig nextLv = config.get((int) (level + 1.0D));
        long nextLevelExp = nextLv.totalExp;

        if (exp > nextLevelExp) {
            Integer lastAnchorInfoId = null;
            try {
                lastAnchorInfoId = userPlayInfoDao.findByUserInfoIdOrCreate(userInfoId).getRecentlyPlayedAnchorInfoId();

                Attrs attrs = RolesCultivateLogic.tryBreakthrough(roles);
                Attrs attrsGlobal = attrsManagement.logAttrsGlobal(
                        userInfoId,
                        attrs,
                        "card:", roles.getCardId(), "xtqy"
                );

                log.debug("渡劫后全局属性为：{}", Attribute.getLogInfo(attrsGlobal));
            } catch (Exception e) {
                log.error("渡劫失败", e);
            }
            ClientManager.sendMessageToClient(lastAnchorInfoId, roles);
        }



        rolesDao.save(roles);
    }

    private void handleEquip(Equip equip) {
        this.roles = rolesDao.findByIdMust(rolesId);

        Integer userInfoId = roles.getUserInfoId();

        int equipId = equip.equipId();
        EquipCfg cfg = Tables.cfg.get_tbequipcfg().get(equipId);
        int equipLevel = cfg.equipLevel;
        int type = cfg.type;
        int price = cfg.recoveryPrice;

        Set<Integer> equipmentSet = roles.rolePanel.cultivatePanel.inventory.get(type);
        if (equipmentSet == null || equipmentSet.isEmpty()) {
            // 如果该类型装备不存在或为空，直接穿戴新装备
            roles.rolePanel.cultivatePanel.inventory.put(type, new HashSet<>(List.of(equipId)));
            log.debug("{} 获得装备{}, 最终穿戴{}", roles.userInfoId, cfg.id, roles.rolePanel.cultivatePanel.inventory.get(type));
            rolesDao.save(roles);
            return;
        }
        Optional<Integer> first = equipmentSet.stream().findFirst();

        EquipCfg currentEquipment = Tables.cfg.get_tbequipcfg().get(first.get());
        if (currentEquipment.equipLevel < equipLevel) {
            // 回收旧的
            for (Integer old : roles.rolePanel.cultivatePanel.inventory.remove(type)) {
                int oldRecoveryPrice = Tables.cfg.get_tbequipcfg().get(old).recoveryPrice;
                ActorRef accountActor = ActorManager.getActor(UserAccountActor.class, userInfoId);
                accountActor.tell(new ReceiveInfo(userInfoId, CurrencyType.ls, oldRecoveryPrice, "装备回收"), ActorRef.noSender());
            }
            roles.rolePanel.cultivatePanel.inventory.put(type, new HashSet<>(List.of(equipId)));
        } else {
            // 回收新的
            ActorRef accountActor = ActorManager.getActor(UserAccountActor.class, userInfoId);
            accountActor.tell(new ReceiveInfo(userInfoId, CurrencyType.ls, price, "装备回收"), ActorRef.noSender());
        }

        log.debug("{} 获得装备{} , 旧的为{}, 最终穿戴{}", roles.userInfoId, cfg.id,
                first.get(),
                roles.rolePanel.cultivatePanel.inventory.get(type));
        rolesDao.save(roles);
    }

    // 技能升级{消费}
    private void handleUpgradeSkill(UpgradeSkill upgradeSkill) {
        this.roles = rolesDao.findByIdMust(rolesId);
        Roles roles = rolesDao.findByIdMust(rolesId);
        UserAccount userAccount = userAccountDao.findByUserInfoId(roles.getUserInfoId());
        SkillLvCostCfg costCfg = Tables.cfg.get_tbskilllvcostcfg().get(roles.getCardId() << 3 + roles.getSkillLv());

        // 检验钱
        for (Map.Entry<Integer, Double> costEntry : costCfg.cost.entrySet()) {
            Integer currency = costEntry.getKey();
            Double cost = costEntry.getValue();
            userAccount.currency[currency] -= cost;
            if (userAccount.currency[currency] < 0) {
                log.info("钱不够");
                return;
            }
        }

        // 支付
        for (Map.Entry<Integer, Double> costEntry : costCfg.cost.entrySet()) {
            Integer currency = costEntry.getKey();
            Double cost = costEntry.getValue();

            ActorRef accountActor = ActorManager.getActor(UserAccountActor.class, userAccount.userInfoId);
            accountActor.tell(new PayInfo(
                    userAccount.userInfoId,
                    currency,
                    cost,
                    "升级%s".formatted(roles.getCardId())
            ), this.self());
        }
        roles.skillLv++;
        log.debug("角色:{}技能等级:{}", roles.getId(), roles.skillLv);
        rolesDao.save(roles);
    }

    /// 释放技能
    private void releaseSkillInfo(SkillInfo skillInfo) {
        ReentrantLock lock = LooksManager.roleActionLocks.getOrDefault(skillInfo.getRolesId(), new ReentrantLock());
        lock.lock();
        try {
            this.roles = rolesDao.findByIdMust(rolesId);
            RolesSkillCfg cfg = skillInfo.getCfg();
            String rolesId = skillInfo.getRolesId();

            int skillId = skillInfo.getSkillId();

            if (CDHandle.isInCD(rolesId, skillId)) {
                long remainingCDTime = CDHandle.getRemainingCDTime(rolesId, skillId);
                log.error("技能正在cd中--{}s", remainingCDTime);
                ClientManager.sendMessageToClient(skillInfo.getAnchorInfoId(), new ActionException("技能正在cd中--" + remainingCDTime + "s"));
                return;
            }

            // 目标校验
            if ((skillInfo.getEffectOnTheEnemy() != null ? skillInfo.getEffectOnTheEnemy().length : 0) > cfg.effectOnTheEnemyNum)
                throw new ActionException("友方目标数量不匹配");
            if ((skillInfo.getEffectOnFriendly() != null ? skillInfo.getEffectOnFriendly().length : 0) > cfg.effectOnFriendlyNum)
                throw new ActionException("敌方目标数量不匹配");

            AttrsBuffCfg buffCfg = cfg.beForBuf;
            if (buffCfg != null) {
                String[] targetIds = switch (buffCfg.beneficiary) {
                    case 0 -> new String[]{rolesId};
                    case 1 -> skillInfo.getEffectOnFriendly();
                    case 2 -> skillInfo.getEffectOnTheEnemy();
                    default -> throw new ActionException("目标类型错误");
                };

                for (String targetId : targetIds) {
                    BuffInfo buffInfo = new BuffInfo(
                            skillInfo.getAnchorInfoId(),
                            skillInfo.getSkillId(),
                            skillInfo.getRolesId(),
                            targetId,
                            buffCfg
                    );
                    buffInfo.setSourceRolesAttrs(roles.rolePanel.getAttribute());
                    log.info("Actor 角色ID: {} 释放了技能-施加buff {}", rolesId, buffInfo);
                    ActorRef targetActor = getRolesActor(targetId);
                    targetActor.tell(buffInfo, self());
                }
            }

            if (skillInfo.getCfg().damage != null) {
                ROLES_SKILLS.skillHurt(skillInfo, skillInfo.getCfg());
            }

            int skillNeedCd = skillInfo.getCfg().cd;
            Double v = this.roles.rolePanel.getAttribute().getOrDefault(AIDCFG.CoolingReduction, 0.0D);
            skillNeedCd = (int) (skillNeedCd * (1 - v));
            CDHandle.scheduleSkillCDReset(rolesId, skillNeedCd);
        } finally {
            lock.unlock();
        }
    }

    /// 受到buff 进行加成
    private void receivedBuff(BuffInfo buffInfo) {
        this.roles = rolesDao.findByIdMust(rolesId);
        // 施法者面板
        Map<Integer, Double> attribute0 = buffInfo.getSourceRolesAttrs();

        // 目标面板
        Map<Integer, Double> attribute1 = roles.rolePanel.getAttribute();

        for (AttrsAddition attrsAddition : buffInfo.getCfg().percentMarkup) {
            int attrs = attrsAddition.attrs;

            Map<Integer, Double> attribute = switch (attrsAddition.sourceOfAttrs) {
                case 0 -> attribute0;
                case 1 -> attribute1;
                default -> throw new ActionException("目标类型错误");
            };

            Integer use = attrsAddition.use;
            Double useValue = attribute.get(use);

            double av = attrsAddition.av / AttributeManagerLogic.ATTRS_NUMBER_TYPE.get(attrs);
            double mv = attrsAddition.mv / 100D;

            double mvv = useValue * mv;
            double value = av + mvv;

            // 保留 2位 小数
            value = new BigDecimal(Double.toString(value)).setScale(2, RoundingMode.HALF_UP).doubleValue();

            buffInfo.addValue.merge(attrs, value, Double::sum);

            String rolesId = buffInfo.rolesId;
            Attrs updateValue = roles.rolePanel.obtainUpdateAttrs(buffInfo.addValue);
            if (updateValue.hasValue()) {
                roles.rolePanel.fightPanel.clear();
                buffInfo.addValue = updateValue;
                roles.rolePanel.fightPanel.buff.add(buffInfo);
                StringBuilder logInfo = Attribute.getLogInfo(updateValue);
                log.info("Actor 角色ID: {} buff生效({}) {}", rolesId, buffInfo.getDuration(), logInfo);
            }
        }

        rolesDao.save(roles);
        ClientManager.sendMessageToClient(buffInfo.getAnchorInfoId(), buffInfo);
    }


    /// 受到伤害
    private void handleHurtInfo(HurtInfo info) {
        this.roles = rolesDao.findByIdMust(rolesId);
        FightingLogic.beHarmed(roles, info.hurt);
        rolesDao.save(roles);
    }
}
